home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Frameworks / TransSkel 3.24 / Source / TransSkel.c < prev    next >
Text File  |  1996-01-17  |  67KB  |  2,502 lines

  1. /*
  2.  * TransSkel - Transportable Macintosh application skeleton
  3.  *
  4.  * Release 3.23
  5.  *
  6.  * Please report problems to Paul DuBois.
  7.  *
  8.  *
  9.  * TransSkel is in the public domain and was originally written by:
  10.  *
  11.  *             Paul DuBois
  12.  *             Wisconsin Regional Primate Research Center
  13.  *             1220 Capitol Court
  14.  *             Madison, WI  53715-1299  USA
  15.  *
  16.  * Internet:    dubois@primate.wisc.edu
  17.  *            
  18.  * Additional changes were made by:
  19.  *    
  20.  *             Owen Hartnett
  21.  *             OHM Software Company
  22.  *             163 Richard Drive
  23.  *             Tiverton, RI 02878  USA
  24.  *         
  25.  * Internet:    omh@cs.brown.edu
  26.  * UUCP:        uunet!brunix!omh    
  27.  *
  28.  * Owen is also responsible for the port to THINK Pascal.
  29.  *
  30.  * Bob Schumaker joined the cast between versions 2.0 and 3.0.
  31.  *
  32.  *             Bob Schumaker
  33.  *             The AMIX Corporation
  34.  *             1881 Landings Drive
  35.  *             Mountain View, CA 94043-0848
  36.  *
  37.  * Internet:    bob@markets.amix.com
  38.  * UUCP:        {sun, uunet, netcom}!markets!bob
  39.  * CIS:            72227,2103
  40.  * AOL:            BSchumaker
  41.  *
  42.  * This version of TransSkel compiles under THINK C 7 or 8 or Metrowerks C 5
  43.  * or 6. (Possibly under THINK C 6 or Metrowerks 4.5 with some minor tweaks.)
  44.  *
  45.  * Reference key:
  46.  * IM        Inside Macintosh
  47.  * TN        Macintosh Technical Notes
  48.  * MHIG        Macintosh Human Interface Guidelines
  49.  * HIN        Human Interface Notes
  50.  * PGMF        Programmer's Guide to MultiFinder (APDA)
  51.  * TPN        TransSkel Programmer's Notes
  52.  *
  53.  * Recent history is given below.  Earlier change history is in TSHistory.
  54.  * If you've been writing applications with an earlier release, READ THAT FILE!
  55.  *
  56.  * 15 May 94 Release 3.17
  57.  * - Changed references to QuickDraw globals so they're written in terms of the
  58.  * qd struct.  E.g., thePort -> qd.thePort, gray -> qd.gray.
  59.  * - Redid some pattern references so they'll compile whether or not
  60.  * dangerousPattern is defined, and if universal headers are used.
  61.  * 25 May 94
  62.  * - Try to cast the argument to InitDialogs() properly depending on whether or
  63.  * not the universal headers are used.
  64.  *
  65.  * 10 Aug 94 Release 3.18
  66.  * - Began adding full support for Universal Headers so TransSkel can be
  67.  * compiled in native PowerPC mode.  I'm doing this under Metrowerks C, so
  68.  * for the moment this also involves porting to Metrowerks.  (For compiling
  69.  * M680x0 code under Metrowerks, no changes were necessary from release 3.17,
  70.  * which was a pleasant surprise.)
  71.  * - skelUnivHeaders invented.  If value is 1, the Universal Headers are
  72.  * available in the header files(and thus UPP definitions are also available),
  73.  * otherwise workarounds are necessary for UPP types.
  74.  * - skelPPC invented.  If value is 1, PPC code is being compiled.  Simpler
  75.  * check than checking for powerc and __powerc.
  76.  * 05 Sep 94
  77.  * - Fixed bug in SkelDialog() with not checking whether the window property
  78.  * type was already attached to the handler.  That could happen when replacing
  79.  * the handler for an existing window, and trying to add the property when one
  80.  * is already there returns an error.
  81.  * 30 Oct 94
  82.  * - Release 3.17 went to the qd form of QuickDraw global access, but that
  83.  * broke THINK Pascal support since THINK Pascal doesn't define the qd struct.
  84.  * References to the QuickDraw globals are now made using SkelQD(global), which
  85.  * can be defined to use the qd or non-qd access forms.  The qd form is still
  86.  * the default, but the THINK Pascal version of the TransSkel library can be
  87.  * compiled easily by supplying a non-qd definition of SkelQD().
  88.  *
  89.  * 22 Dec 94 Release 3.19
  90.  * - Cleaned up the source code a little (e.g., removed some unused variables).
  91.  * 20 Mar 95
  92.  * - Updated to compile under universal headers release 2, which differ
  93.  * somewhat from release 1.  These headers are supplied with Metrowerks 5.0
  94.  * and 5.5.
  95.  * - Converted old routine names (e.g., GetIText()) to new names (e.g.,
  96.  * GetDialogItemText().  This may make TransSkel not compile under THINK C 6.
  97.  * 25 May 95 Release 3.20
  98.  * - Updated for Metrowerks 6.
  99.  * - Updated for Symantec 8.0 C++ (PPC) and THINK C 8.0 (68K).
  100.  * 30 Jun 95 Release 3.22
  101.  * - Modified SkelInit() to properly set inForeground when the application is
  102.  * launched directly into the background.  Code is based on that supplied by
  103.  * Hans van der Meer.
  104.  * - Added SkelGestaltCheck() function.
  105.  * 15 Jul 95
  106.  * - Modified SkelInit() to test for presence of Thread Manager and to set the
  107.  * hasThreads variable.  The value of this variable can be obtained by passing
  108.  * the new selector skelQHasThreads to SkelQuery().  Modified main event loop
  109.  * to call Thread Manager, and added new functions SkelSetThreadTimes() and
  110.  * SkelGetThreadTimes() for setting and getting the amound of time spent in
  111.  * the Thread Manager.  Code is based on that supplied by Hans van der Meer.
  112.  */
  113.  
  114. /*
  115.  * This should be "#define useThreads 0" to turn off Thread Manager support,
  116.  * e.g., if you can't get TransSkel.c to compile.  Also, make sure there is
  117.  * no "#define useThreads 1" in your compiler prefix.
  118.  */
  119.  
  120. # ifndef useThreads
  121. # define useThreads    0
  122. # endif
  123.  
  124. /*
  125.  * TransSkel.h contains defines, typedefs, and public function prototypes
  126.  */
  127.  
  128. # include    "TransSkel.h"
  129.  
  130.  
  131. # include    <Traps.h>
  132.  
  133. # if skelUnivHeaders > 1
  134. # include    <Gestalt.h>
  135. # else
  136. # include    <GestaltEqu.h>
  137. # endif
  138.  
  139. # include    <EPPC.h>
  140.  
  141. # if useThreads
  142. # include    <Threads.h>
  143. # if skelUnivHeaders > 1
  144. # include    <CodeFragments.h>
  145. # else
  146. # include    <FragLoad.h>
  147. # endif
  148.  
  149. /*
  150.  * You may need to define some of these if they're not in your headers.
  151.  */
  152. /*# define gestaltThreadMgrAttr            'thds'*/
  153. /*# define gestaltThreadMgrPresent        0*/
  154. /*# define gestaltThreadsLibraryPresent    2*/
  155.  
  156. # endif /* useThreads */
  157.  
  158.  
  159. /*
  160.  * New(TypeName) returns handle to new object, for any TypeName.
  161.  * If there is insufficient memory, the result is nil.
  162.  */
  163.  
  164. # define    New(type)    (type **) NewHandle ((Size) sizeof (type))
  165.  
  166.  
  167. /* -------------- */
  168. /* Internal types */
  169. /* -------------- */
  170.  
  171.  
  172. /*
  173.  * Private data types for window and menu handlers
  174.  */
  175.  
  176. typedef struct WHandler    WHandler, *WHPtr, **WHHandle;
  177.  
  178. struct WHandler
  179. {
  180.     WindowPtr                whWind;        /* window/dialog to handle */
  181.     SkelWindMouseProcPtr    whMouse;    /* mouse-click handler */
  182.     SkelWindKeyProcPtr        whKey;        /* key-click handler */
  183.     SkelWindUpdateProcPtr    whUpdate;    /* update handler */
  184.     SkelWindActivateProcPtr    whActivate;    /* activate event handler */
  185.     SkelWindCloseProcPtr    whClose;    /* close "event" handler */
  186.     SkelWindClobberProcPtr    whClobber;    /* window disposal proc */
  187.     SkelWindIdleProcPtr        whIdle;        /* main loop idle proc */
  188.     SkelWindZoomProcPtr        whZoom;        /* zoom proc */
  189.     SkelWindSelectProcPtr    whSelect;    /* item selection proc (dialog) */
  190.     ModalFilterProcPtr            whFilter;    /* event filter proc (dialog) */
  191.     Rect        whGrow;                    /* limits on window sizing */
  192.     Boolean        whSized;                /* true = window was resized */
  193.     Boolean        whFrontOnly;            /* idle only when window active */
  194.     short        whFlags;                /* various flags */
  195.     SkelWindPropHandle    whProperties;    /* property list */
  196.     WHHandle    whNext;                    /* next window handler */
  197. };
  198.  
  199. typedef struct MHandler    MHandler, *MHPtr, **MHHandle;
  200.  
  201. struct MHandler
  202. {
  203.     short                    mhID;        /* menu id */
  204.     SkelMenuSelectProcPtr    mhSelect;    /* item selection handler */
  205.     SkelMenuClobberProcPtr    mhClobber;    /* menu disposal proc */
  206.     Boolean        mhSubMenu;                /* whether submenu */
  207.     MHHandle    mhNext;                    /* next menu handler */
  208. };
  209.  
  210.  
  211. /* ------------------------------------------- */
  212. /* Prototypes for internal (private) functions */
  213. /* ------------------------------------------- */
  214.  
  215. static WHHandle GetWHandler (WindowPtr w);
  216. static void DetachWHandler (WHHandle wh);
  217.  
  218. static void RouteEvent (EventRecord *evt);
  219.  
  220. static void DoMenuCommand (long command);
  221. static void DoMenuHook (void);
  222.  
  223. static void DoMouse (WHHandle h, EventRecord *evt);
  224. static void DoKey (WHHandle h, char ch, unsigned char code, short mods);
  225. static void DoUpdate (EventRecord *evt);
  226. static void DoActivate (EventRecord *evt);
  227. static void DoClose (WHHandle h);
  228. static void DoClobber (WHHandle h);
  229. static void DoDlogEvt (DialogPtr dlog, EventRecord *evt);
  230. static Boolean DoDlogFilter (DialogPtr dlog, EventRecord *evt);
  231.  
  232. static void DoGrow (WHHandle h, Point startPt);
  233. static void DoZoom (WHHandle h, short partCode);
  234.  
  235.  
  236. /* ------------------ */
  237. /* Internal variables */
  238. /* ------------------ */
  239.  
  240.  
  241. /*
  242.  * Window and menu handler variables.
  243.  *
  244.  * whList and mhList are the lists of window and menu handlers.
  245.  * mhClobOnRmve is true if the menu handler disposal proc
  246.  * is to be called when a handler is removed.  It is temporarily set
  247.  * false when handlers are installed for menus that already
  248.  * have handlers - the old handler is removed WITHOUT calling the
  249.  * disposal proc.  The effect is to replace the handler for the menu
  250.  * without destroying the menu itself.
  251.  *
  252.  * dragRect determines the limits on window dragging.  It is set in
  253.  * SkelInit() to the bounding box of the desktop region inset by 4 pixels.
  254.  *
  255.  * growRect contains the default limits on window sizing.  It is set in
  256.  * SkelInit().  The lower limits on window sizing of 80 pixels both directions
  257.  * is sufficient to allow text windows room to draw a grow box and scroll
  258.  * bars without having the thumb and arrows overlap.  The upper limits are
  259.  * determined from the screen size. (Probably incorrectly for the case of > 1
  260.  * screen.)
  261.  * These default values may be changed if with SkelGrowBounds if they are
  262.  * not appropriate.
  263.  *
  264.  * zoomProc is the default zoom procedure to use if the window does not have
  265.  * one of its own.  zoomProc may be nil, in which case the default is to zoom
  266.  * to just about full window size.
  267.  *
  268.  * mhDrawBarOnRmve determines whether the menu bar is redrawn by
  269.  * SkelRmveMenu() after taking a menu out of the menu bar.  Normally
  270.  * it's true, but SkelClobber() sets it false temporarily to avoid
  271.  * flicker as each menu is removed.
  272.  */
  273.  
  274.  
  275. static WHHandle    whList = (WHHandle) nil;
  276. static Rect        dragRect;
  277. static Rect        growRect;
  278. static SkelWindZoomProcPtr    zoomProc = (SkelWindZoomProcPtr) nil;
  279.  
  280. static WindowPtr    oldWindow = (WindowPtr) nil;     
  281. static WHHandle        oldWHandler = (WHHandle) nil;
  282.  
  283.  
  284. static MHHandle    mhList = (MHHandle) nil;
  285. static Boolean    mhClobOnRmve = true;
  286. static Boolean    mhDrawBarOnRmve = true;
  287.  
  288.  
  289. /*
  290.  * Miscellaneous
  291.  *
  292.  * - skelEnv contains SysEnvirons() information.
  293.  * - sysVersion contains the system software version.
  294.  * - hasGestalt is true if Gestalt() is supported.
  295.  * - has64KROM is true if the current machine has the 64K ROM.
  296.  * - hasGetWVariant is true if GetWVariant() is supported.
  297.  * - mBarHeight is menu bar height.  Window sizing, zooming and dragging
  298.  * code takes this into account.  Initialized in SkelInit(), which see
  299.  * for teeth-gnashing over such a simple thing.
  300.  * - doneFlag determines when SkelEventLoop() returns.  It is set by calling
  301.  * SkelStopEventLoop(), which is how the host requests a halt.
  302.  * - pIdle points to a background procedure, to be run during event
  303.  * processing.  Set it with SkelSetIdle().  If nil, there's no
  304.  * procedure.
  305.  * - pEvent points to an event-inspecting hook, to be run whenever an
  306.  * event occurs.  Set it with SkelSetEventHook().  If nil, there's no
  307.  * procedure.
  308.  * - eventMask controls the event types requested by GetNextEvent() or
  309.  * WaitNextEvent() in SkelEventLoop().
  310.  * - pMenuHook points to a procedure called whenever a menu selection is about
  311.  * to be executed.  nil if no hook.
  312.  * - diskInitPt is the location at which the disk initialization dialog
  313.  * appears, if an uninitialized disk is inserted.
  314.  * - eventModifiers is the value of the modifiers field of the current event.
  315.  * - eventPtr points to the current event (nil if none seen yet).
  316.  * - defInitParams contains the default SkelInit() parameters if caller passes
  317.  * nil.
  318.  */
  319.  
  320. static SysEnvRec    skelEnv;
  321. static long    sysVersion = 0;
  322. static Boolean    hasGestalt;
  323. static Boolean    has64KROM;
  324. static Boolean    hasGetWVariant;
  325. static short    mBarHeight;
  326. static short    doneFlag;
  327. static short    eventMask = everyEvent ^ keyUpMask;
  328. static short    eventModifiers = 0;
  329. static EventRecord    *eventPtr = (EventRecord *) nil;
  330. static Point    diskInitPt = { /* v = */ 120, /* h = */ 100 };
  331.  
  332. static SkelIdleProcPtr            pIdle = (SkelIdleProcPtr) nil;
  333. static SkelEventHookProcPtr        pEvent = (SkelEventHookProcPtr) nil;
  334. static SkelMenuHookProcPtr        pMenuHook = (SkelMenuHookProcPtr) nil;
  335.  
  336. static SkelInitParams    defInitParams =
  337. {
  338.     6,                            /* no. of times to call MoreMasters() */
  339.     (GrowZoneUPP) nil,            /* GrowZone proc */
  340.     (SkelResumeProcPtr) nil,    /* resume proc */
  341.     0L                            /* stack adjustment */
  342. };
  343.  
  344. /*
  345.  * Multitasking support stuff
  346.  *
  347.  * - hasWNE is true if WaitNextEvent() is available.
  348.  * - inForeground is true if application is running in foreground (not
  349.  * suspended).  Initially true unless application is launched directly
  350.  * into the background.
  351.  * - fgWaitTime and bgWaitTime are WaitNextEvent() times for foreground and
  352.  * background states.
  353.  * - getFrontClicks indicates whether the application wants to receive
  354.  * content-area clicks that bring it to the foreground.
  355.  * - pSuspendResume is the application function to call when suspend and
  356.  * resume events occur.
  357.  * - pClipCvt is the function to call for clipboard conversion.
  358.  * - hasThreads is true if the Thread Manager is available.
  359.  * - fgThreadTime and bgThreadTime indicate the number of ticks to spend
  360.  * in the Thread Manager each passs through the main event loop, for
  361.  * foreground and background states.  These are initially zero, on the
  362.  * assumption that the application is not multithreaded.  The application
  363.  * may set them non-zero to give more time to the Thread Manager.  (Note
  364.  * that the event loop is coded such that the Thread Manager is not locked
  365.  * out even if the times are zero; it will simply be called only once each
  366.  * time through the loop.)
  367.  */
  368.  
  369. static Boolean    hasWNE;
  370. static Boolean    inForeground = true;
  371. static long        fgWaitTime = 6L;            /* 0.1 seconds */
  372. static long        bgWaitTime = 300L;            /* 5.0 seconds */
  373. static Boolean    getFrontClicks = false;
  374. static SkelSuspendResumeProcPtr    pSuspendResume = (SkelSuspendResumeProcPtr) nil;
  375. static SkelClipCvtProcPtr    pClipCvt = (SkelClipCvtProcPtr) nil;
  376.  
  377. static Boolean    hasThreads = false;
  378. static long        fgThreadTime = 0L;
  379. static long        bgThreadTime = 0L;
  380.  
  381. /*
  382.  * Apple Event support
  383.  *
  384.  * - hasAppleEvents is true if Apple Events are available.
  385.  * - pAEHandler is the function to call when an Apple Event occurs.
  386.  */
  387.  
  388. static Boolean    hasAppleEvents = false;
  389. static SkelAEHandlerProcPtr    pAEHandler = (SkelAEHandlerProcPtr) nil;
  390.  
  391.  
  392. /* --------------------------- */
  393. /* Initialization and shutdown */
  394. /* --------------------------- */
  395.  
  396. /*
  397.  * Initialize the various Macintosh Managers and lots of other stuff.
  398.  *
  399.  * FlushEvents does NOT toss disk insert events; this is so disks
  400.  * inserted while the application is starting up don't result
  401.  * in dead drives.
  402.  *
  403.  * initParams contains initialization parameters:
  404.  * - the number of times to call MoreMasters
  405.  * - the address of a grow zone procedure to call if memory allocation
  406.  * problems occur (nil if none to be used)
  407.  * - the address of a resume procedure to pass to InitDialogs()
  408.  * (nil if none is to be used)
  409.  * - amount to adjust the application stack size by (default 0; no adjustment)
  410.  *
  411.  * if initParams is nil, defaults are used.
  412.  */
  413.  
  414. pascal void
  415. SkelInit (SkelInitParamsPtr initParams)
  416. {
  417. ProcessSerialNumber    psn1, psn2;
  418. Boolean                answer;
  419. EventRecord    dummyEvent;
  420. Handle        h;
  421. short        i;
  422. # if useThreads
  423. long        result;
  424. # endif
  425.  
  426.     if (initParams == (SkelInitParams *) nil)
  427.         initParams = &defInitParams;
  428.  
  429.     if (initParams->skelGzProc != (GrowZoneUPP) nil)
  430.         SetGrowZone (initParams->skelGzProc);
  431.  
  432.     SetApplLimit (GetApplLimit () - initParams->skelStackAdjust);
  433.  
  434.     MaxApplZone ();
  435.  
  436.     for (i = 0; i < initParams->skelMoreMasters; i++)
  437.         MoreMasters ();
  438.  
  439.     FlushEvents (everyEvent - diskMask, 0 );
  440.     InitGraf (&SkelQD (thePort));
  441.     InitFonts ();
  442.     InitWindows ();
  443.     InitMenus ();
  444.     TEInit ();
  445.  
  446.     /*
  447.      * If universal headers are used, InitDialogs() no longer takes
  448.      * a meaningful argument.  Pass zero.
  449.      */
  450.  
  451. # if skelUnivHeaders
  452.     InitDialogs (0);
  453. # else
  454.     InitDialogs (initParams->skelResumeProc);
  455. # endif
  456.     InitCursor ();
  457.  
  458.     (void) SysEnvirons (1, &skelEnv);
  459.     
  460.     sysVersion = (long) skelEnv.systemVersion;
  461.  
  462.     has64KROM = (skelEnv.machineType == envMac || skelEnv.machineType == envXL);
  463.  
  464.     /*
  465.      * If 64K ROM machine, use hard-coded value of 20.  Otherwise use
  466.      * Script Manager routine GetMBarHeight().  (This assumes, just to be
  467.      * safe, that GetMBarHeight() glue doesn't return 20 on 64K ROM systems,
  468.      * which it very well may.  The low memory variable MBarHeight (0x0BAA)
  469.      * isn't used because it doesn't exist on 64K ROM machines (TN OV 4, p.7).
  470.      */
  471.  
  472.     mBarHeight = (has64KROM ? 20 : GetMBarHeight ());
  473.  
  474.     /*
  475.      * Determine whether WaitNextEvent() is implemented (TN's OV 16 and TN TB 14)
  476.      */
  477.  
  478.     if (has64KROM)
  479.         hasWNE = false;
  480.     else
  481.         hasWNE = SkelTrapAvailable (_WaitNextEvent);
  482.  
  483.     hasGestalt = SkelTrapAvailable (_Gestalt);
  484. /*
  485.     hasAppleEvents = hasGestalt
  486.                     && Gestalt (gestaltAppleEventsAttr, &result) == noErr
  487.                     && (result & (1 << gestaltAppleEventsPresent));
  488. */
  489.     hasAppleEvents = SkelGestaltCheck (gestaltAppleEventsAttr,
  490.                                         gestaltAppleEventsPresent);
  491.  
  492. # if useThreads
  493. # if skelPPC
  494.     hasThreads = hasGestalt
  495.                     && Gestalt (gestaltThreadMgrAttr, &result) == noErr
  496.                     && (result & (1 << gestaltThreadsLibraryPresent))
  497.                     /*
  498.                      * If your compiler won't compile next line, change
  499.                      * kUnresolvedSymbolAddress to 0
  500.                      */
  501.                     && ((Ptr) NewThread != (Ptr) kUnresolvedSymbolAddress)
  502.                     && (result & (1 << gestaltThreadMgrPresent));
  503. # else
  504.     hasThreads = hasGestalt
  505.                     && Gestalt (gestaltThreadMgrAttr, &result) == noErr
  506.                     && (result & (1 << gestaltThreadMgrPresent));
  507. # endif /* skelPPC */
  508. # endif /* useThreads */
  509.  
  510.     /*
  511.      * Determine whether GetWVariant() exists for checking whether a dialog is
  512.      * a movable modal or not.  The variant code can be gotten other ways, but
  513.      * the existence of trap precedes the existence of movalable modal windows,
  514.      * so if the trap doesn't exist, movable modals aren't likely to, either.
  515.      */
  516.  
  517.     hasGetWVariant = SkelTrapAvailable (_GetWVariant);
  518.  
  519.     /*
  520.      * Check whether application wants to get "bring to front" clicks.
  521.      */
  522.  
  523.     if ((h = GetResource ('SIZE', -1)) != (Handle) nil)
  524.     {
  525.         getFrontClicks = (((**(short **) h) & 0x200) != 0);
  526.         ReleaseResource (h);
  527.     }
  528.  
  529.     /*
  530.      * Window dragging limits are determined from bounding box of desktop.
  531.      * Upper limits of window sizing are related to that.  Both can involve
  532.      * multiple monitors, and should allow for menu bar.  dragRect is inset
  533.      * so as to leave at least 4 pixels of window title bar visible in both
  534.      * directions (IM I-289).
  535.      *
  536.      * GetGrayRgn() bounding box gives desktop extents.  On 64K ROM
  537.      * machines, GetGrayRgn() might not be present; could use GrayRgn
  538.      * bounding box, but use screenBits.bounds - menu bar, to avoid
  539.      * low memory access.  The two should be equivalent.
  540.      */
  541.  
  542.     if (has64KROM)
  543.     {
  544.         dragRect = SkelQD (screenBits.bounds);
  545.         dragRect.top += mBarHeight;
  546.     }
  547.     else
  548.     {
  549.         /* GetGrayRgn () already takes menu bar into account */
  550.         dragRect = (**GetGrayRgn ()).rgnBBox;
  551.     }
  552.  
  553.     SetRect (&growRect, 80, 80,
  554.                 dragRect.right - dragRect.left,
  555.                 dragRect.bottom - dragRect.top);
  556.  
  557.     InsetRect (&dragRect, 4, 4);
  558.  
  559.     /*
  560.      * If Process Manager is present, query it as to initial foreground/background
  561.      * status, overriding default setting of inForeground = true.
  562.      */
  563.     if (SkelGestaltCheck (gestaltOSAttr, gestaltLaunchControl)
  564.             && GetCurrentProcess (&psn1) == noErr
  565.             && GetFrontProcess(&psn2) == noErr
  566.             && SameProcess (&psn1, &psn2, &answer) == noErr )        
  567.         inForeground = answer;
  568.  
  569.     /*
  570.      * Let application come to front in multitasking environment, TN TB 35, p.8.
  571.      * (Assuming it wants to, that is.)
  572.      */
  573.  
  574.     (void) EventAvail (everyEvent, &dummyEvent);
  575.     (void) EventAvail (everyEvent, &dummyEvent);
  576.     (void) EventAvail (everyEvent, &dummyEvent);
  577.     (void) EventAvail (everyEvent, &dummyEvent);
  578. }
  579.  
  580.  
  581. /*
  582.  * Copy the default initialization parameters into the structure
  583.  * pointed to by initParams.
  584.  */
  585.  
  586. pascal void
  587. SkelGetInitParams (SkelInitParamsPtr initParams)
  588. {
  589.     *initParams = defInitParams;
  590. }
  591.  
  592.  
  593. /*
  594.  * Clobber all the menu, window and dialog handlers.  Tell SkelRmveMenu()
  595.  * not to redraw menu bar so it doesn't flicker as menus are removed,
  596.  * then redraw it manually.
  597.  *
  598.  * Before removing window handlers, hide all the windows.  Do this from
  599.  * back to front (more esthetic and speedier).  If a window belongs to a DA,
  600.  * close the DA.  (For early systems (e.g., 4.1), if you leave a DA open,
  601.  * the system crashes the next time you try to open that DA.)
  602.  */
  603.  
  604. pascal void
  605. SkelCleanup (void)
  606. {
  607. Boolean    oldFlag;
  608. short    theKind;
  609. WindowPeek    w;
  610. WindowPtr    lastVis;
  611.  
  612.     for (;;)
  613.     {
  614.         lastVis = (WindowPtr) nil;
  615.         for (w = (WindowPeek) FrontWindow (); w != (WindowPeek) nil; w = w->nextWindow)
  616.         {
  617.             if (w->visible)
  618.                 lastVis = (WindowPtr) w;
  619.         }
  620.         if (lastVis == (WindowPtr) nil)        /* no more visible windows */
  621.             break;
  622.         if (lastVis != (WindowPtr) nil)
  623.         {
  624.             theKind = ((WindowPeek) lastVis)->windowKind;
  625.             if (theKind < 0)                /* DA, close it */
  626.                 CloseDeskAcc (theKind);
  627.             else
  628.                 HideWindow (lastVis);
  629.         }
  630.     }
  631.  
  632.     while (whList != (WHHandle) nil)
  633.         SkelRmveWind ((**whList).whWind);
  634.  
  635.     oldFlag = mhDrawBarOnRmve;
  636.     mhDrawBarOnRmve = false;
  637.     while (mhList != (MHHandle) nil)
  638.         SkelRmveMenu (GetMenuHandle((**mhList).mhID));
  639.     mhDrawBarOnRmve = oldFlag;
  640.     DrawMenuBar ();
  641. }
  642.  
  643.  
  644. /* ----------------------------------- */
  645. /* Execution environment interrogation */
  646. /* ----------------------------------- */
  647.  
  648.  
  649.  
  650. #define trapMask    0x0800
  651.  
  652. static short
  653. NumToolboxTraps (void)
  654. {
  655.     if (NGetTrapAddress (_InitGraf, ToolTrap)
  656.         == NGetTrapAddress (0xaa6e, ToolTrap))
  657.         return (0x200);
  658.     return (0x400);
  659. }
  660.  
  661.  
  662. static TrapType
  663. GetTrapType (short theTrap)
  664. {
  665.     return ((theTrap & trapMask) ? ToolTrap : OSTrap);
  666. }
  667.  
  668.  
  669. pascal Boolean
  670. SkelTrapAvailable (short theTrap)
  671. {
  672. TrapType    tType;
  673.  
  674.     if ((tType = GetTrapType (theTrap)) == ToolTrap)
  675.     {
  676.         theTrap &= 0x07ff;
  677.         if (theTrap >= NumToolboxTraps ())
  678.             theTrap = _Unimplemented;
  679.     }
  680.     return (NGetTrapAddress (theTrap, tType)
  681.                 != NGetTrapAddress (_Unimplemented, ToolTrap));
  682. }
  683.  
  684.  
  685. /*
  686.  * This function returns true if the specific selector is present and
  687.  * the bit denoted by the featureCode is set. Note that featureCode can
  688.  * be -1 if you just want to check for the presence of the selector,
  689.  * without any interest in its value.
  690.  */
  691.  
  692. pascal Boolean 
  693. SkelGestaltCheck (OSType selector, short featureCode)
  694. {
  695. long    result;
  696.     
  697.     if (!hasGestalt || Gestalt (selector, &result) != noErr)
  698.         return (false);
  699.         
  700.     return ( featureCode < 0 ) ? true : 
  701.                 ( (result & (1 << featureCode)) ? true : false );
  702. }
  703.  
  704.  
  705. /*
  706.  * Query the TransSkel execution environment.  Shouldn't be called until
  707.  * after SkelInit() has been called.  Result is undefined if selector isn't
  708.  * legal.
  709.  */
  710.  
  711. pascal long
  712. SkelQuery (short selector)
  713. {
  714. long    result;
  715. Rect    r;
  716. RgnHandle    rgn;
  717.  
  718.     switch (selector)
  719.     {
  720.     case skelQVersion:
  721.         result = ((long) skelMajorRelease << 16) | skelMinorRelease;
  722.         break;
  723.     case skelQSysVersion:
  724.         result = sysVersion;
  725.         break;
  726.     case skelQHasWNE:
  727.         result = hasWNE ? 1 : 0;
  728.         break;
  729.     case skelQHas64KROM:
  730.         result = has64KROM ? 1 : 0;
  731.         break;
  732.     case skelQMBarHeight:
  733.         result = mBarHeight;
  734.         break;
  735.     case skelQHasColorQD:
  736.         result = skelEnv.hasColorQD ? 1 : 0;
  737.         break;
  738.     case skelQQDVersion:
  739.         /* get QuickDraw version number */
  740.         if (!hasGestalt
  741.             || Gestalt (gestaltQuickdrawVersion, &result) != noErr)
  742.             result = 0;                    /* assume original QuickDraw */
  743.         break;
  744.     case skelQInForeground:
  745.         result = inForeground ? 1 : 0;
  746.         break;
  747.     case skelQHasGestalt:
  748.         result = hasGestalt ? 1 : 0;
  749.         break;
  750.     case skelQHasAppleEvents:
  751.         result = hasAppleEvents ? 1 : 0;
  752.         break;
  753.     case skelQGrayRgn:
  754.         rgn = NewRgn ();
  755.         if (rgn != (RgnHandle) nil)
  756.         {
  757.             if (has64KROM)
  758.             {
  759.                 r = SkelQD (screenBits.bounds);
  760.                 r.top += mBarHeight;
  761.                 RectRgn (rgn, &r);
  762.             }
  763.             else
  764.             {
  765.                 /* GetGrayRgn () already takes menu bar into account */
  766.                 CopyRgn (GetGrayRgn (), rgn);
  767.             }
  768.         }
  769.         result = (long) rgn;
  770.         break;
  771.     case skelQHasThreads:
  772.         result = hasThreads ? 1 : 0;
  773.         break;
  774.     default:
  775.         /* result is undefined! */
  776.         break;
  777.     }
  778.     return (result);
  779. }
  780.  
  781. /* ------------------------------------- */
  782. /* Event loop initiation and termination */
  783. /* ------------------------------------- */
  784.  
  785.  
  786. /*
  787.  * Main event loop.
  788.  *
  789.  * - Take care of DA's with SystemTask() if necessary.
  790.  * - Get an event.
  791.  * - Pass event to event router.
  792.  * - Call the Thread Manager, if present.
  793.  *    
  794.  * doneFlag is restored to its previous value upon exit.  This allows
  795.  * SkelEventLoop() to be called recursively.
  796.  */
  797.  
  798. pascal void
  799. SkelEventLoop (void)
  800. {
  801. EventRecord    evt;
  802. Boolean        oldDoneFlag;
  803. long        waitTime;
  804. long        nextEventCheckTime = 0L;
  805.  
  806.     oldDoneFlag = doneFlag;        /* save in case this is a recursive call */
  807.     doneFlag = false;            /* set for this call */
  808.     while (!doneFlag)
  809.     {
  810.         if (TickCount () >= nextEventCheckTime)
  811.         {
  812.             if (hasWNE)
  813.             {
  814.                 waitTime = (inForeground ? fgWaitTime : bgWaitTime);
  815.                 (void) WaitNextEvent (eventMask, &evt, waitTime, nil);
  816.             }
  817.             else
  818.             {
  819.                 /*
  820.                  * On some early versions of the system software, it cannot
  821.                  * be assumed that the event contains a null event if the
  822.                  * GetNextEvent() return value is false.  GetNextEvent() calls
  823.                  * SystemEvent() to handle some DA events, and returns false
  824.                  * if the event was handled.  However, in such cases the event
  825.                  * record may still have the event that occurred, *not* a null
  826.                  * event.  To avoid problems later with misinterpreting the
  827.                  * event as non-null, force it to look like a null event.
  828.                  */
  829.                 SystemTask ();
  830.                 if (!GetNextEvent (eventMask, &evt))
  831.                     evt.what = nullEvent;
  832.             }
  833.     
  834.             SkelRouteEvent (&evt);
  835.  
  836.             nextEventCheckTime = TickCount ()
  837.                         + (inForeground ? fgThreadTime : bgThreadTime);
  838.         }
  839.  
  840. # if useThreads
  841.         if (hasThreads)
  842.             YieldToAnyThread ();
  843. # endif
  844.     }
  845.     doneFlag = oldDoneFlag;    /* restore in case this was a recursive call */
  846. }
  847.  
  848.  
  849. /*
  850.  * Tell current instance of SkelEventLoop() to drop dead
  851.  */
  852.  
  853. pascal void
  854. SkelStopEventLoop (void)
  855. {
  856.     doneFlag = true;
  857. }
  858.  
  859.  
  860. /* ----------------- */
  861. /* Event dispatching */
  862. /* ----------------- */
  863.  
  864.  
  865. /*
  866.  * Route a single event and run window idle procedures.
  867.  *
  868.  * If the event is a null-event, call the "no-event" handler for the front
  869.  * window and for any other windows with idle procedures that are always
  870.  * supposed to run.  This is done in such a way that it is safe for idle
  871.  * procs to remove the window handler for their own window if they want
  872.  * (unlikely, but...).
  873.  */
  874.  
  875. pascal void
  876. SkelRouteEvent (EventRecord *evt)
  877. {
  878. WHHandle    wh, wh2;
  879. GrafPtr        tmpPort;
  880. WindowPtr    w;
  881. SkelWindIdleProcPtr    p;
  882.  
  883.     RouteEvent (evt);
  884.  
  885.     /*
  886.      * Run applicable window idle procs.  Make sure to save and restore
  887.      * the port, since idle procs for the non-active window may be run.
  888.      */
  889.  
  890.     if (evt->what == nullEvent)
  891.     {
  892.         GetPort (&tmpPort);
  893.         for (wh = whList; wh != (WHHandle) nil; wh = wh2)
  894.         {
  895.             wh2 = (**wh).whNext;
  896.             w = (**wh).whWind;
  897.             if (w == FrontWindow () || !(**wh).whFrontOnly)
  898.             {
  899.                 if ((p = (**wh).whIdle) != (SkelWindIdleProcPtr) nil)
  900.                 {
  901.                     if (!hasWNE)
  902.                         SystemTask ();
  903.                     SetPort (w);
  904.                     (*p) ();
  905.                 }
  906.             }
  907.         }
  908.         SetPort (tmpPort);
  909.     }
  910. }
  911.  
  912.  
  913. /*
  914.  * General event dispatch routine.
  915.  *
  916.  * If there is an event-handling hook and it handles the event, the
  917.  * event is not further processed here.  Otherwise, run the application's idle
  918.  * time procedure if the event is a null event, then process the event.
  919.  *
  920.  * Null events are sent through DialogSelect() if a dialog is active.  This
  921.  * is necessary to make sure  the caret blinks if a dialog has any editText
  922.  * items.
  923.  *
  924.  * Network events are not supported as per the deprecation in TN NW 07.
  925.  * Application-defined events 1, 2 and 3 are not handled, either.
  926.  */
  927.  
  928. static void
  929. RouteEvent (EventRecord *evt)
  930. {
  931. Point        evtPt;
  932. GrafPtr        evtPort;
  933. short        evtPart;
  934. short        evtMods;
  935. char        evtChar;
  936. long        evtMsge;
  937. unsigned char evtCode;
  938. WHHandle    wh;
  939. WindowPtr    frontWind;
  940. Boolean        frontIsDlog;
  941. short        osMsge;
  942. Boolean        osResume;
  943. Boolean        osClipCvt;
  944. Rect        r1, r2;
  945. WStateData    **wdh;
  946. SignedByte    state;
  947.  
  948.     /* save values for SkelGetCurrentEvent() and SkelGetModifiers() */
  949.  
  950.     eventPtr = evt;
  951.     eventModifiers = evt->modifiers;
  952.  
  953.     /* don't bother handling event below if event hook does so here */
  954.     
  955.     if (pEvent != (SkelEventHookProcPtr) nil && (*pEvent) (evt))
  956.         return;
  957.  
  958.     frontWind = FrontWindow ();
  959.     frontIsDlog = SkelIsDlog (frontWind);
  960.  
  961.     evtPt = evt->where;
  962.     evtMods = evt->modifiers;
  963.     evtMsge = evt->message;
  964.  
  965.     switch (evt->what)
  966.     {
  967.     case nullEvent:
  968.         /*
  969.          * Run the application idle-time function.  If the front window is
  970.          * a dialog window, pass the event to the dialog event handler; this
  971.          * is necessary to make the caret blink if it has an edit text item.
  972.          * Don't use frontWind after calling the idle-time function, since
  973.          * the function might change the front window!
  974.          */
  975.         if (pIdle != (SkelIdleProcPtr) nil)
  976.             (*pIdle) ();
  977.         if (SkelIsDlog (FrontWindow ()))
  978.             DoDlogEvt (FrontWindow (), evt);
  979.         break;
  980.  
  981.     /*
  982.      * Mouse click.  Get the window in which the click occurred, and
  983.      * the part of the window.
  984.      */
  985.     case mouseDown:
  986.         evtPart = FindWindow (evtPt, &evtPort);
  987.         wh = GetWHandler (evtPort);
  988.  
  989.         /*
  990.          * Beep if a click occurs outside of a movable modal dialog box.
  991.          * Exceptions: allow clicks in menu bar, and command-clicks in
  992.          * drag region of underlying windows.
  993.          */
  994.  
  995.         if (SkelIsMMDlog (frontWind)
  996.             && !PtInRgn (evtPt, ((WindowPeek) frontWind)->strucRgn))
  997.         {
  998.             if (evtPart != inMenuBar
  999.                 && !(evtPart == inDrag && evtPort != frontWind && (evtMods & cmdKey)))
  1000.             {
  1001.                 SysBeep (1);
  1002.                 break;
  1003.             }
  1004.         }
  1005.  
  1006.         switch (evtPart)
  1007.         {
  1008.  
  1009.         /*
  1010.          * Click in desk accessory window.  Pass back to the system.
  1011.          */
  1012.         case inSysWindow:
  1013.             SystemClick (evt, evtPort);
  1014.             break;
  1015.  
  1016.         /*
  1017.          * Click in menu bar.  Track the mouse and execute
  1018.          * selected command, if any.
  1019.          */
  1020.         case inMenuBar:
  1021.             DoMenuHook ();
  1022.             DoMenuCommand (MenuSelect (evtPt));
  1023.             break;
  1024.  
  1025.         /*
  1026.          * Click in grow box.  Resize window.
  1027.          */
  1028.         case inGrow:
  1029.             DoGrow (wh, evtPt);
  1030.             break;
  1031.  
  1032.         /*
  1033.          * Click in title bar.  Drag the window around.
  1034.          * Problem fix:  DragWindow() seems to call StillDown()
  1035.          * first, so that clicks in drag regions while machine is
  1036.          * busy don't otherwise bring window to front if the mouse
  1037.          * is already up by the time DragWindow() is called.  So the
  1038.          * window is selected first to make sure it's at least
  1039.          * activated (unless the command key is down, IM I-289).
  1040.          *
  1041.          * Also offset the window's userState by the amount of the drag
  1042.          * (it'd be simpler to set it to the final content rect but the
  1043.          * window might be in zoomed state rather than user state).
  1044.          */
  1045.         case inDrag:
  1046.             if (evtPort != frontWind && (evtMods & cmdKey) == 0)
  1047.                 SelectWindow (evtPort);
  1048.             SkelGetWindContentRect (evtPort, &r1);        /* post-drag position */
  1049.             DragWindow (evtPort, evtPt, &dragRect);
  1050.             SkelGetWindContentRect (evtPort, &r2);        /* post-drag position */
  1051.             wdh = (WStateData **)(((WindowPeek) evtPort)->dataHandle);
  1052.             state = HGetState ((Handle) wdh);
  1053.             HLock ((Handle) wdh);
  1054.             OffsetRect (&(**wdh).userState, r2.left - r1.left, r2.top - r1.top);
  1055.             HSetState ((Handle) wdh, state);
  1056.             break;
  1057.  
  1058.         /*
  1059.          * Click in close box.  Call the close proc if the window
  1060.          * has one.
  1061.          */
  1062.         case inGoAway:
  1063.             if (TrackGoAway (evtPort, evtPt))
  1064.                 DoClose (wh);
  1065.             break;
  1066.  
  1067.         /*
  1068.          * Click in zoom box.  Track the click and then zoom the
  1069.          * window if necessary.
  1070.          */
  1071.         case inZoomIn:
  1072.         case inZoomOut:
  1073.             if (TrackBox (evtPort, evtPt, evtPart))
  1074.                 DoZoom (wh, evtPart);
  1075.             break;
  1076.  
  1077.         /*
  1078.          * Click in content region.  If the window wasn't frontmost
  1079.          * (active), just select it, otherwise pass the click to the
  1080.          * window's mouse click handler.  Exception: if the application
  1081.          * wants to receive content clicks event in non-frontmost windows,
  1082.          * select the window and "repeat" the click.
  1083.          */
  1084.         case inContent:
  1085.             if (evtPort != frontWind)
  1086.             {
  1087.                 SelectWindow (evtPort);
  1088.                 if (!getFrontClicks)    /* don't pass click to handler */
  1089.                     break;
  1090.                 SetPort (evtPort);
  1091.             }
  1092.             if (frontIsDlog)
  1093.                 DoDlogEvt (evtPort, evt);
  1094.             else
  1095.                 DoMouse (wh, evt);
  1096.             break;
  1097.  
  1098.         }
  1099.         break;    /* mouseDown */
  1100.  
  1101.     /*
  1102.      * Key down event.  If the command key was down, process as menu
  1103.      * item selection, otherwise pass the character and the modifiers
  1104.      * flags to the active window's key handler.
  1105.      *
  1106.      * Command-period is not supposed to be used as a menu-item equivalent.
  1107.      * Consequently, that's noticed as a special case and not passed to
  1108.      * the menu routines.
  1109.      */
  1110.     case keyDown:
  1111.     case autoKey:
  1112.         evtChar = evtMsge & charCodeMask;
  1113.         evtCode = (evtMsge & keyCodeMask) >> 8;    /* hope bit 7 isn't set! */
  1114.  
  1115.         if ((evtMods & cmdKey) && !SkelCmdPeriod (evt))    /* try menu equivalent */
  1116.         {
  1117.             DoMenuHook ();
  1118.             DoMenuCommand (MenuKey (evtChar));
  1119.             break;
  1120.         }
  1121.  
  1122.         if (frontIsDlog)
  1123.             DoDlogEvt (frontWind, evt);
  1124.         else
  1125.             DoKey (GetWHandler (frontWind), evtChar, evtCode, evtMods);
  1126.         break;
  1127.  
  1128.     /*
  1129.      * Key up event.  Key-ups are signified by setting the high bit
  1130.      * of the key code.  This never executes unless the application
  1131.      * changes the system event mask *and* the TransSkel event mask.
  1132.      */
  1133.     case keyUp:
  1134.         evtChar = evtMsge & charCodeMask;            /* probably 0? */
  1135.         evtCode = ((evtMsge & keyCodeMask) >> 8) | 0x80;
  1136.  
  1137.         if (frontIsDlog)
  1138.             DoDlogEvt (frontWind, evt);
  1139.         else
  1140.             DoKey (GetWHandler (frontWind), evtChar, evtCode, evtMods);
  1141.         break;
  1142.  
  1143.     /*
  1144.      * Update a window.
  1145.      */
  1146.     case updateEvt:
  1147.         DoUpdate (evt);
  1148.         break;
  1149.  
  1150.     /*
  1151.      * Activate or deactivate a window.
  1152.      */
  1153.     case activateEvt:
  1154.         DoActivate (evt);
  1155.         break;
  1156.  
  1157.     /*
  1158.      * handle inserts of uninitialized disks.  Deactivate the frontmost
  1159.      * window since the disk-init dialog doesn't do anything with
  1160.      * activate events for other windows.
  1161.      */
  1162.     case diskEvt:
  1163.         if (HiWord (evtMsge) != noErr)
  1164.         {
  1165.             SkelActivate (FrontWindow (), false);
  1166.             DILoad ();
  1167.             (void) DIBadMount (diskInitPt, evtMsge);
  1168.             DIUnload ();
  1169.         }
  1170.         break;
  1171.  
  1172.     case osEvt:                /* aka app4Evt aka MultiFinder event */
  1173.         /* rip the message field into constituent parts */
  1174.         osMsge = ((evtMsge >> 24) & 0xff);            /* high byte */
  1175.         osResume = (Boolean) ((evtMsge & resumeFlag) != 0);
  1176.         osClipCvt = (Boolean) ((evtMsge & convertClipboardFlag) != 0);
  1177.  
  1178.         switch (osMsge)
  1179.         {
  1180.         case suspendResumeMessage:
  1181.             /*
  1182.              * Tell application it's being suspended or resumed
  1183.              * Tell application to convert scrap if necessary
  1184.              */
  1185.         
  1186.             inForeground = osResume;
  1187.             if (pSuspendResume != (SkelSuspendResumeProcPtr) nil)
  1188.                 (*pSuspendResume) (inForeground);
  1189.             if (!osResume)            /* always convert on suspend */
  1190.                 osClipCvt = true;
  1191.             if (osClipCvt && pClipCvt != (SkelClipCvtProcPtr) nil)
  1192.                 (*pClipCvt) (inForeground);
  1193.             break;
  1194.  
  1195.         case mouseMovedMessage:
  1196.             /* recompute mouse region -- not implemented */
  1197.             break;
  1198.  
  1199.         /*
  1200.          * 0xfd is a child-died event -- not implemented here since it's
  1201.          * only had limited use, e.g., by certain debuggers.  The child pid
  1202.          * is byte 2 ((evtMsge >> 16) & 0xff)
  1203.         case 0xfd:
  1204.             break;
  1205.          */
  1206.  
  1207.         default:                /* other OS event */
  1208.             /* pass event to catch-all handler -- not implemented */
  1209.             break;
  1210.         }
  1211.         break;
  1212.  
  1213.     case kHighLevelEvent:
  1214.         if (pAEHandler != (SkelAEHandlerProcPtr) nil)
  1215.             (*pAEHandler) (evt);
  1216.         break;
  1217.     }
  1218. }
  1219.  
  1220.  
  1221. /*
  1222.  * Activate or deactivate a window by synthesizing a fake
  1223.  * activate event and sending it through the event router.
  1224.  * Useful for activating a window when you don't know its
  1225.  * activate function.
  1226.  */
  1227.  
  1228. pascal void
  1229. SkelActivate (WindowPtr w, Boolean active)
  1230. {
  1231. EventRecord    evt;
  1232.  
  1233.     if (w != (WindowPtr) nil)
  1234.     {
  1235.         evt.what = activateEvt;
  1236.         evt.modifiers = active ? activeFlag : 0;
  1237.         evt.when = TickCount ();
  1238.         SetPt (&evt.where, 0, 0);
  1239.         evt.message = (long) w;
  1240.         SkelRouteEvent (&evt);
  1241.     }
  1242. }
  1243.  
  1244.  
  1245. /*
  1246.  * Call a window's close procedure.  Useful for closing a window when you
  1247.  * don't know its close function.
  1248.  *
  1249.  * This function knows how to close Desk Accessories.
  1250.  */
  1251.  
  1252. pascal void
  1253. SkelClose (WindowPtr w)
  1254. {
  1255.     if (w != (WindowPtr) nil)
  1256.     {
  1257.         if (((WindowPeek) w)->windowKind < 0)        /* DA window */
  1258.             CloseDeskAcc (((WindowPeek) w)->windowKind);
  1259.         else
  1260.             DoClose (GetWHandler (w));
  1261.     }
  1262. }
  1263.  
  1264.  
  1265. /*
  1266.  * Set the TransSkel event mask.  Does not have anything to do with the
  1267.  * system event mask.  See TPN 3.
  1268.  */
  1269.  
  1270. pascal void
  1271. SkelSetEventMask (short mask)
  1272. {
  1273.     eventMask = mask;
  1274. }
  1275.  
  1276.  
  1277. /*
  1278.  * Return the event mask.
  1279.  */
  1280.  
  1281. pascal short
  1282. SkelGetEventMask (void)
  1283. {
  1284.     return (eventMask);
  1285. }
  1286.  
  1287.  
  1288. /*
  1289.  * Install an idle-time task.  If p is nil, the current task is
  1290.  * disabled.
  1291.  */
  1292.  
  1293. pascal void
  1294. SkelSetIdle (SkelIdleProcPtr p)
  1295. {
  1296.     pIdle = p;
  1297. }
  1298.  
  1299.  
  1300. /*
  1301.  * Return the current idle-time task.  Return nil if none.
  1302.  */
  1303.  
  1304. pascal SkelIdleProcPtr
  1305. SkelGetIdle (void)
  1306. {
  1307.     return (pIdle);
  1308. }
  1309.  
  1310.  
  1311. /*
  1312.  * Install an event-inspecting hook.  If p is nil, the hook is
  1313.  * disabled.
  1314.  */
  1315.  
  1316. pascal void
  1317. SkelSetEventHook (SkelEventHookProcPtr p)
  1318. {
  1319.     pEvent = p;
  1320. }
  1321.  
  1322.  
  1323. /*
  1324.  * Return the current event-inspecting hook.  Return nil if none.
  1325.  */
  1326.  
  1327. pascal SkelEventHookProcPtr
  1328. SkelGetEventHook (void)
  1329. {
  1330.     return (pEvent);
  1331. }
  1332.  
  1333.  
  1334. pascal void
  1335. SkelSetSuspendResume (SkelSuspendResumeProcPtr p)
  1336. {
  1337.     pSuspendResume = p;
  1338. }
  1339.  
  1340.  
  1341. pascal SkelSuspendResumeProcPtr
  1342. SkelGetSuspendResume (void)
  1343. {
  1344.     return (pSuspendResume);
  1345. }
  1346.  
  1347.  
  1348. pascal void
  1349. SkelSetClipCvt (SkelClipCvtProcPtr p)
  1350. {
  1351.     pClipCvt = p;
  1352. }
  1353.  
  1354.  
  1355. pascal SkelClipCvtProcPtr
  1356. SkelGetClipCvt (void)
  1357. {
  1358.     return (pClipCvt);
  1359. }
  1360.  
  1361.  
  1362. pascal void
  1363. SkelSetWaitTimes (long fgTime, long bgTime)
  1364. {
  1365.     fgWaitTime = fgTime;
  1366.     bgWaitTime = bgTime;
  1367. }
  1368.  
  1369.  
  1370. pascal void
  1371. SkelGetWaitTimes (long *pFgTime, long *pBgTime)
  1372. {
  1373.     if (pFgTime != (long) nil)
  1374.         *pFgTime = fgWaitTime;
  1375.     if (pBgTime != (long) nil)
  1376.         *pBgTime = bgWaitTime;
  1377. }
  1378.  
  1379.  
  1380. pascal void
  1381. SkelSetThreadTimes (long fgTime, long bgTime)
  1382. {
  1383.     fgThreadTime = fgTime;
  1384.     bgThreadTime = bgTime;
  1385. }
  1386.  
  1387.  
  1388. pascal void
  1389. SkelGetThreadTimes (long *pFgTime, long *pBgTime)
  1390. {
  1391.     if (pFgTime != (long) nil)
  1392.         *pFgTime = fgThreadTime;
  1393.     if (pBgTime != (long) nil)
  1394.         *pBgTime = bgThreadTime;
  1395. }
  1396.  
  1397.  
  1398. pascal EventRecord *
  1399. SkelGetCurrentEvent (void)
  1400. {
  1401.     return (eventPtr);
  1402. }
  1403.  
  1404.  
  1405. pascal short
  1406. SkelGetModifiers (void)
  1407. {
  1408.     return (eventModifiers);
  1409. }
  1410.  
  1411.  
  1412. pascal void
  1413. SkelSetAEHandler (SkelAEHandlerProcPtr p)
  1414. {
  1415.     pAEHandler = p;
  1416. }
  1417.  
  1418.  
  1419. pascal SkelAEHandlerProcPtr
  1420. SkelGetAEHandler (void)
  1421. {
  1422.     return (pAEHandler);
  1423. }
  1424.  
  1425.  
  1426. /* -------------------------------------------------------------------- */
  1427. /*                    Window-handler event routing routines                */
  1428. /*                                                                        */
  1429. /*    See manual for discussion of port-setting behavior: the current        */
  1430. /*    port is set to a window when it becomes active in DoActivate().        */
  1431. /* -------------------------------------------------------------------- */
  1432.  
  1433.  
  1434. /*
  1435.  * Process dialog event.  dlog is the dialog to which the event applies.
  1436.  * Give the filter a chance at the event first.  If the filter doesn't
  1437.  * handle it, pass the event to DialogSelect().  If DialogSelect() selects
  1438.  * an item, pass the item to the window's item selection function, if
  1439.  * there is one.  This is used to dispose of dialog events that aren't
  1440.  * handled in some other more direct fashion.
  1441.  */
  1442.  
  1443.  
  1444. static void
  1445. DoDlogEvt (DialogPtr dlog, EventRecord *evt)
  1446. {
  1447. short        item;
  1448. WHHandle    wh;
  1449. SkelWindSelectProcPtr    select;
  1450.  
  1451.     if (DoDlogFilter (dlog, evt))
  1452.         return;
  1453.  
  1454.     if (DialogSelect (evt, &dlog, &item)
  1455.        && (wh = GetWHandler (dlog)) != (WHHandle) nil
  1456.        && (select = (**wh).whSelect) != (SkelWindSelectProcPtr) nil)
  1457.     {
  1458.         (*select) (dlog, item);
  1459.     }
  1460. }
  1461.  
  1462. /*
  1463.  * Run a dialog's filter function to give the filter first chance
  1464.  * at the event.
  1465.  *
  1466.  * The filter function returns false if it doesn't handle the event.
  1467.  * It returns true if it handled the event, in which case it should
  1468.  * set the item parameter.  The item will be passed to the dialog's
  1469.  * item selection function.
  1470.  *
  1471.  * If the filter function returns true, look up the handler again
  1472.  * just in case the filter function also called SkelRmveDlog().
  1473.  * If it did, the handler will have become invalid.  Looking it
  1474.  * up again avoids disaster.
  1475.  */
  1476.  
  1477. static Boolean
  1478. DoDlogFilter (DialogPtr dlog, EventRecord *evt)
  1479. {
  1480. short        item;
  1481. WHHandle    wh;
  1482. SkelWindSelectProcPtr    select;
  1483. ModalFilterProcPtr        filter;
  1484. Boolean    result = false;
  1485.  
  1486.     if ((wh = GetWHandler (dlog)) != (WHHandle) nil
  1487.         && (filter = (**wh).whFilter) != (ModalFilterProcPtr) nil)
  1488.     {
  1489.         if ((*(ModalFilterProcPtr) filter) (dlog, evt, &item))
  1490.         {
  1491.             if ((wh = GetWHandler (dlog)) != (WHHandle) nil
  1492.                 && (select = (**wh).whSelect) != (SkelWindSelectProcPtr) nil)
  1493.             {
  1494.                 (*select) (dlog, item);
  1495.             }
  1496.             result = true;
  1497.         }
  1498.     }
  1499.     return (result);
  1500. }
  1501.  
  1502.  
  1503. /*
  1504.  * Pass local mouse coordinates, click time, and the modifiers flag
  1505.  * word to the handler.  Should not be necessary to set the port, as
  1506.  * the click is passed to the active window's handler.
  1507.  */
  1508.  
  1509. static void
  1510. DoMouse (WHHandle h, EventRecord *evt)
  1511. {
  1512. Point    thePt;
  1513.  
  1514.     if (h != (WHHandle) nil && (**h).whMouse != (SkelWindMouseProcPtr) nil)
  1515.     {
  1516.         thePt = evt->where;    /* make local copy */
  1517.         GlobalToLocal (&thePt);
  1518.         (*(**h).whMouse) (thePt, evt->when, evt->modifiers);
  1519.     }
  1520. }
  1521.  
  1522.  
  1523. /*
  1524.  * Pass the character code, key code and the modifiers flag word to
  1525.  * the handler. Should not be necessary to set the port, as the click
  1526.  * is passed to the active window's handler.
  1527.  */
  1528.  
  1529. static void
  1530. DoKey (WHHandle h, char c, unsigned char code, short mods)
  1531. {
  1532.     if (h != (WHHandle) nil && (**h).whKey != (SkelWindKeyProcPtr) nil)
  1533.         (*(**h).whKey) ((short) c, (short) code, mods);
  1534. }
  1535.  
  1536.  
  1537. /*
  1538.  * Call the window updating procedure, passing to it an indicator whether
  1539.  * the window has been resized or not.  Then clear the flag, assuming
  1540.  * the update proc took whatever action was necessary to respond to
  1541.  * resizing.
  1542.  *
  1543.  * The Begin/EndUpdate stuff is done to clear the update region even if
  1544.  * the handler doesn't have any update proc.  Otherwise the Window
  1545.  * Manager will keep generating update events for the window, stalling  
  1546.  * updates of other windows.
  1547.  *
  1548.  * For dialog windows, UpdtDialog() does the normal item updating.  The
  1549.  * filter procedure can take care of non-item drawing, such as a bold
  1550.  * outline around a default button.
  1551.  *
  1552.  * Saves, sets, and restore the port, since it's not always the
  1553.  * active window that is updated.
  1554.  */
  1555.  
  1556. static void
  1557. DoUpdate (EventRecord *evt)
  1558. {
  1559. WHHandle    h;
  1560. GrafPtr    port;
  1561. GrafPtr    tmpPort;
  1562.  
  1563.     port = (WindowPtr) evt->message;
  1564.  
  1565.     GetPort (&tmpPort);
  1566.     SetPort (port);
  1567.     BeginUpdate (port);
  1568.     if (SkelIsDlog (port))
  1569.     {
  1570.         if (!DoDlogFilter (port, evt))
  1571.             UpdateDialog (port, port->visRgn);    /* let Dialog Manager finish update */
  1572.     }
  1573.     else
  1574.     {
  1575.         h = GetWHandler (port);
  1576.         if (h != (WHHandle) nil)
  1577.         {
  1578.             if ((**h).whUpdate != (SkelWindUpdateProcPtr) nil)
  1579.                 (*(**h).whUpdate) ((**h).whSized);
  1580.             (**h).whSized = false;
  1581.         }
  1582.     }
  1583.     EndUpdate (port);
  1584.     SetPort (tmpPort);
  1585. }
  1586.  
  1587.  
  1588. /*
  1589.  * Pass activate/deactivate notification to handler.  On activate,
  1590.  * set the port to the window coming active.  Normally this is done by
  1591.  * the user clicking in a window.
  1592.  *
  1593.  * *** BUT ***
  1594.  * Under certain conditions, a deactivate may be generated for a window
  1595.  * that has never had the port set to it by a preceding activate.  For
  1596.  * instance, if an application puts up window A, then window B in front
  1597.  * of A, then starts processing events, the first events will be a
  1598.  * deactivate for A and an activate for B.  Therefore, since it can't be
  1599.  * assumed that an activate ever set the port to A, the port needs to be
  1600.  * set for deactivates as well so drawing occurs in the correct port.
  1601.  *
  1602.  * This matters not a whit for the more usual cases that occur.  If a
  1603.  * deactivate for one window is followed by an activate for another, the
  1604.  * port will still be switched properly to the newly active window.  If
  1605.  * no activate follows the deactivate, the deactivated window is the last
  1606.  * one, and it doesn't matter what the port ends up set to, anyway.
  1607.  *
  1608.  * On deactivate, port is saved and restored in case deactivate is due to
  1609.  * a modal dialog having been brought in front and port changed to it
  1610.  * explicitly by the application.  The deactivate shouldn't leave the port
  1611.  * changed away from the dialog!
  1612.  *
  1613.  * For dialogs, DoDlogEvt() is called, allowing DialogSelect() to do
  1614.  * whatever it does for dialog activates.  The handler's activate procedure
  1615.  * is called in addition to this (e.g., to hilite controls or text selections,
  1616.  * adjust menus).
  1617.  */
  1618.  
  1619. static void
  1620. DoActivate (EventRecord *evt)
  1621. {
  1622. WHHandle    h;
  1623. GrafPtr    port;
  1624. GrafPtr    tmpPort;
  1625. Boolean    active;
  1626.  
  1627.     active = (evt->modifiers & activeFlag);
  1628.     port = (WindowPtr) evt->message;
  1629.  
  1630.  
  1631.     GetPort (&tmpPort);    /* save so can restore if deactivate */
  1632.     SetPort (port);
  1633.     if (SkelIsDlog (port))
  1634.         DoDlogEvt (port, evt);
  1635.     else
  1636.     {
  1637.         h = GetWHandler (port);
  1638.         if (h != (WHHandle) nil)
  1639.         {
  1640.             if ((**h).whActivate != (SkelWindActivateProcPtr) nil)
  1641.                 (*(**h).whActivate) (active);
  1642.         }
  1643.     }
  1644.     if (!active)
  1645.         SetPort (tmpPort);
  1646. }
  1647.  
  1648.  
  1649. /*
  1650.  * Execute a window handler's close box proc.  The close proc for
  1651.  * handlers for temp windows that want to remove themselves when the
  1652.  * window is closed can call SkelRmveWind to dispose of the window
  1653.  * and remove the handler from the window handler list.  Thus, windows
  1654.  * may be dynamically created and destroyed without filling up the
  1655.  * handler list with a bunch of invalid handlers.
  1656.  *
  1657.  * If the handler doesn't have a close proc, just hide the window.
  1658.  * The host should provide some way of reopening the window (perhaps
  1659.  * a menu selection).  Otherwise the window will be lost from user
  1660.  * control if it is hidden, since it won't receive user-initiated
  1661.  * events.
  1662.  *
  1663.  * This is called both for regular and dialog windows.
  1664.  *
  1665.  * Normally this is invoked because the close box of the active window
  1666.  * is clicked, in which case the port will be set to the window.  However,
  1667.  * SkelClose() allows the application to close an aritrary window, not just
  1668.  * the frontmost one -- so the port is saved and restored.
  1669.   */
  1670.  
  1671. static void
  1672. DoClose (WHHandle h)
  1673. {
  1674. GrafPtr    tmpPort;
  1675.  
  1676.     if (h != (WHHandle) nil)
  1677.     {
  1678.         GetPort (&tmpPort);
  1679.         SetPort ((**h).whWind);
  1680.         if ((**h).whClose != (SkelWindCloseProcPtr) nil)
  1681.             (*(**h).whClose) ();
  1682.         else
  1683.             HideWindow ((**h).whWind);
  1684.         SetPort (tmpPort);
  1685.     }
  1686. }
  1687.  
  1688.  
  1689. /*
  1690.  * Execute a window handler's clobber proc.  This is called both
  1691.  * for regular and dialog windows.
  1692.  *
  1693.  * Saves sets, and restores the port, since any window (not just active
  1694.  * one) may be clobbered at any time.
  1695.  *
  1696.  * Don't need to check whether handler is nil, as in other handler
  1697.  * procedures, since this is only called by SkelRmveWind with a
  1698.  * known-valid handler.
  1699.  */
  1700.  
  1701. static void
  1702. DoClobber (WHHandle h)
  1703. {
  1704. GrafPtr    tmpPort;
  1705.  
  1706.     GetPort (&tmpPort);
  1707.     SetPort ((**h).whWind);
  1708.     if ((**h).whClobber != (SkelWindClobberProcPtr) nil)
  1709.         (*(**h).whClobber) ();
  1710.     SetPort (tmpPort);
  1711. }
  1712.  
  1713.  
  1714. /*
  1715.  * Handlers for window events not requiring application handler routines
  1716.  * to be called.
  1717.  */
  1718.  
  1719.  
  1720. /*
  1721.  * Have either zoomed a window or sized it manually.  Invalidate
  1722.  * it to force an update and set the 'resized' flag in the window
  1723.  * handler true.  The port is assumed to be set to the port that changed
  1724.  * size.  Handler is assumed non-nil.
  1725.  */
  1726.  
  1727. static void
  1728. TriggerUpdate (WHHandle h)
  1729. {
  1730. GrafPtr    port = (**h).whWind;
  1731.  
  1732.     InvalRect (&port->portRect);
  1733.     (**h).whSized = true;
  1734. }
  1735.  
  1736.  
  1737. /*
  1738.  * Size a window, using the grow limits in the handler record.
  1739.  *
  1740.  * The portRect is invalidated to force an update event.  The window's
  1741.  * update handler procedure should check the parameter passed to it to
  1742.  * check whether the window has changed size, if it needs to adjust
  1743.  * itself to the new size.  THIS IS A CONVENTION.  Update procs must
  1744.  * notice grow "events", there is no procedure specifically for that.
  1745.  *
  1746.  * The clipping rectangle is not reset.  If the host application
  1747.  * keeps the clipping set equal to the portRect or something similar,
  1748.  * then it will have to arrange to treat window growing with more
  1749.  * care.
  1750.  *
  1751.  * Since the grow region of only the active window may be clicked,
  1752.  * it should not be necessary to set the port.
  1753.  */
  1754.  
  1755. static void
  1756. DoGrow (WHHandle h, Point startPt)
  1757. {
  1758. GrafPtr    growPort;
  1759. Rect    growRect;
  1760. long    growRes;
  1761.  
  1762.     if (h != (WHHandle) nil)
  1763.     {
  1764.         growPort = (**h).whWind;
  1765.         growRect = (**h).whGrow;
  1766.  
  1767.         /* growRes will be zero if the size was not actually changed */
  1768.     
  1769.         if ((growRes = GrowWindow (growPort, startPt, &growRect)) != 0)
  1770.         {
  1771.             SizeWindow (growPort, LoWord (growRes), HiWord (growRes), false);
  1772.             TriggerUpdate (h);
  1773.         }
  1774.     }
  1775. }
  1776.  
  1777.  
  1778. /*
  1779.  * Zoom the current window.  Very similar to DoGrow, but window
  1780.  * is erased before zooming for nicer visual effect (per IM IV-50,
  1781.  * TN TB 30, p.4).
  1782.  * 
  1783.  * Normally, since only the active window has a visible zoom box and
  1784.  * TransSkel sets the port to active window, this routine is triggered
  1785.  * by user-initiated clicks in zoom box and the port will be set to
  1786.  * the zoomed window.
  1787.  *
  1788.  * However, it is possible for zooms to be software initiated by the
  1789.  * application itself on any window; for such cases the port needs
  1790.  * to be saved and set before the zoom and restored afterward.
  1791.  */
  1792.  
  1793. static void
  1794. DoZoom (WHHandle h, short zoomDir)
  1795. {
  1796. GrafPtr    w;
  1797. GrafPtr    tmpPort;
  1798. Rect    r, growRect;
  1799.  
  1800.     if (h != (WHHandle) nil)
  1801.     {
  1802.         w = (**h).whWind;
  1803.         GetPort (&tmpPort);                    /* save port and set to */
  1804.         SetPort (w);                        /* zoomed window */
  1805.         if ((**h).whZoom != (SkelWindZoomProcPtr) nil)
  1806.             ((**h).whZoom) (w, zoomDir);    /* custom zoom proc */
  1807.         else if (zoomProc != (SkelWindZoomProcPtr) nil)
  1808.             (*zoomProc) (w, zoomDir);        /* custom default zoom proc */
  1809.         else                                /* default zooming */
  1810.         {
  1811.             EraseRect (&w->portRect);
  1812.             if (zoomDir == inZoomOut)    /* zooming to default state */
  1813.             {
  1814.                 /*
  1815.                  * Get the usable area of the device containing most of the
  1816.                  * window.  (Can ignore the result because the rect is always
  1817.                  * correct.  Pass nil for device parameter because it's
  1818.                  * irrelevant.)  Then adjust rect for title bar height, and
  1819.                  * inset it slightly.
  1820.                  */
  1821.                 (void) SkelGetWindowDevice (w, (GDHandle *) nil, &r);
  1822.                 r.top += SkelGetWindTitleHeight (w) - 1;
  1823.                 /* leave 3-pixel border */
  1824.                 InsetRect (&r, 3, 3);
  1825.                 /* clip to grow limits */
  1826.                 growRect = (**h).whGrow;
  1827.                 growRect.left = growRect.top = 0;
  1828.                 OffsetRect (&growRect, r.left, r.top);
  1829.                 SectRect (&r, &growRect, &r);
  1830.                 (**(WStateData **)(((WindowPeek)w)->dataHandle)).stdState = r;
  1831.             }
  1832.             ZoomWindow (w, zoomDir, false);
  1833.         }
  1834.         SetPort (tmpPort);        /* restore original port */
  1835.         TriggerUpdate (h);
  1836.     }
  1837. }
  1838.  
  1839.  
  1840. /* --------------------------------------------------------- */
  1841. /* Window handler installation/removal/modification routines */
  1842. /* --------------------------------------------------------- */
  1843.  
  1844.  
  1845. /*
  1846.  * Install handler for a window and set current port to it.  Remove
  1847.  * any previous handler for it.  Pass the following parameters:
  1848.  *
  1849.  * w
  1850.  *        Pointer to the window to be handled.  Must be created by host.
  1851.  * doMouse
  1852.  *        Proc to handle mouse clicks in window.  The proc will be
  1853.  *         passed the point (in local coordinates), the time of the
  1854.  *         click, and the modifier flags word.
  1855.  * doKey
  1856.  *        Proc to handle key clicks in window.  The proc will be passed
  1857.  *         the character and the modifier flags word.
  1858.  * doUpdate
  1859.  *        Proc for updating window.  TransSkel brackets calls to update
  1860.  *         procs with calls to BeginUpdate and EndUpdate, so the visRgn
  1861.  *         is set up correctly.  A flag is passed indicating whether the
  1862.  *         window was resized or not.  BY CONVENTION, the entire portRect
  1863.  *         is invalidated when the window is resized or zoomed.  That way,
  1864.  *         the handler's update proc can redraw the entire content region
  1865.  *         without interference from BeginUpdate/EndUpdate.  The flag
  1866.  *         is set to false after the update proc is called; the
  1867.  *         assumption is made that the proc will notice the resizing and
  1868.  *         respond appropriately.
  1869.  * doActivate
  1870.  *        Proc to execute when window is activated or deactivated.
  1871.  *         A boolean is passed to it which is true if the window is
  1872.  *         coming active, false if it's going inactive.
  1873.  * doClose
  1874.  *        Proc to execute when mouse clicked in close box.  Useful
  1875.  *         mainly to temp window handlers that want to know when to
  1876.  *         self-destruct (with SkelRmveWind).
  1877.  * doClobber
  1878.  *        Proc for disposal of handler's data structures
  1879.  * doWIdle
  1880.  *        Proc to execute when no events are pending.
  1881.  * idleFrontOnly
  1882.  *        True if doWIdle should execute on no events only when
  1883.  *         w is frontmost, false if executes all the time.  Note
  1884.  *         that if it always goes, everything else may be slowed down!
  1885.  *
  1886.  * If a particular procedure is not needed (e.g., key events are
  1887.  * not processed by a handler), pass nil in place of the appropriate
  1888.  * procedure address.
  1889.  *
  1890.  * Return true if successful, false if no handler could be allocated.
  1891.  * If false is returned, the port will not have been changed.
  1892.  */
  1893.  
  1894. pascal Boolean
  1895. SkelWindow (WindowPtr w,
  1896.             SkelWindMouseProcPtr doMouse,
  1897.             SkelWindKeyProcPtr doKey,
  1898.             SkelWindUpdateProcPtr doUpdate,
  1899.             SkelWindActivateProcPtr doActivate,
  1900.             SkelWindCloseProcPtr doClose,
  1901.             SkelWindClobberProcPtr doClobber,
  1902.             SkelWindIdleProcPtr doWIdle,
  1903.             Boolean idleFrontOnly)
  1904. {
  1905. WHHandle    whNew, whCur;
  1906. SkelWindPropHandle    wph = (SkelWindPropHandle) nil;
  1907.  
  1908.     /* Get new handler immediately, fail if can't allocate */
  1909.  
  1910.     if ((whNew = New (WHandler)) == (WHHandle) nil)
  1911.         return (false);
  1912.  
  1913.     /*
  1914.      * If there's a current handler for the window, remove it, but first
  1915.      * grab the property list from it so it can be transferred to the new
  1916.      * handler.
  1917.      */
  1918.     
  1919.     if ((whCur = GetWHandler (w)) != (WHHandle) nil)
  1920.     {
  1921.         wph = (**whCur).whProperties;
  1922.         (**whCur).whProperties = (SkelWindPropHandle) nil;
  1923.         DetachWHandler (whCur);
  1924.     }
  1925.  
  1926.     /*
  1927.      * Attach new handler to list of handlers.  It is attached to the
  1928.      * beginning of the list, which is simpler; the order is presumably
  1929.      * irrelevant to the host, anyway.
  1930.      *
  1931.      * Then fill in handler fields (including properties attached to any
  1932.      * previous handler).
  1933.      */
  1934.  
  1935.     (**whNew).whNext = whList;
  1936.     whList = whNew;
  1937.  
  1938.     (**whNew).whWind = w;
  1939.     (**whNew).whMouse = doMouse;
  1940.     (**whNew).whKey = doKey;
  1941.     (**whNew).whUpdate = doUpdate;
  1942.     (**whNew).whActivate = doActivate;
  1943.     (**whNew).whClose = doClose;
  1944.     (**whNew).whClobber = doClobber;
  1945.     (**whNew).whZoom = (SkelWindZoomProcPtr) nil;
  1946.     (**whNew).whIdle = doWIdle;
  1947.     (**whNew).whGrow = growRect;
  1948.     (**whNew).whSized = false;
  1949.     (**whNew).whFrontOnly = idleFrontOnly;
  1950.     (**whNew).whFlags = 0;
  1951.     (**whNew).whProperties = wph;
  1952.     (**whNew).whSelect = (SkelWindSelectProcPtr) nil;
  1953.     (**whNew).whFilter = (ModalFilterProcPtr) nil;
  1954.     SetPort (w);
  1955.  
  1956.     return (true);
  1957. }
  1958.  
  1959.  
  1960. /*
  1961.  * Remove a window handler.  This calls the handler's window disposal
  1962.  * routine and then takes the handler out of the handler list and
  1963.  * disposes of it (including its property list).
  1964.  *
  1965.  * SkelRmveWind is also called by SkelRmveDlog.
  1966.  */
  1967.  
  1968. pascal void
  1969. SkelRmveWind (WindowPtr w)
  1970. {
  1971. WHHandle    h;
  1972.  
  1973.     if ((h = GetWHandler (w)) == (WHHandle) nil)
  1974.         return;
  1975.  
  1976.     DoClobber (h);                                /* call disposal routine */
  1977.     SkelRmveWindProp (w, skelWPropAll);    /* toss properties */
  1978.  
  1979.     DetachWHandler (h);    /* remove handler for window from list */
  1980. }
  1981.  
  1982.  
  1983. /*
  1984.  * Install a handler for a modeless or movable modal dialog window and set
  1985.  * the port to it.  Remove any previous handler for it. SkelDialog calls
  1986.  * SkelWindow as a subsidiary to install a window handler, then sets
  1987.  * the event procedure on return.  A property is also added to the window
  1988.  * to indicate that it's a modeless or movable modal dialog.
  1989.  *
  1990.  * Pass the following parameters:
  1991.  *
  1992.  * dlog
  1993.  *        Pointer to the dialog to be handled.  Must be created by host.
  1994.  * doFilter
  1995.  *        Filter procedure to look at events before they are otherwise
  1996.  *        processed.
  1997.  * doSelect
  1998.  *        Procedure to execute when an item is "selected" (e.g., a mouse
  1999.  *        click occurs in it).
  2000.  * doClose
  2001.  *        Procedure to execute when mouse clicked in close box.  Useful
  2002.  *         mainly to dialog handlers that want to know when to
  2003.  *         self-destruct (with SkelRmveDlog).
  2004.  * doClobber
  2005.  *        Procedure for disposal of handler's data structures
  2006.  *
  2007.  * If a particular procedure is not needed, pass nil in place of
  2008.  * the appropriate procedure address.
  2009.  *
  2010.  * Return true if successful, false if no handler could be allocated.
  2011.  * If false is returned, the port will not have been changed.
  2012.  */
  2013.  
  2014. pascal Boolean
  2015. SkelDialog (DialogPtr dlog,
  2016.             ModalFilterProcPtr doFilter,
  2017.             SkelWindSelectProcPtr doSelect,
  2018.             SkelWindCloseProcPtr doClose,
  2019.             SkelWindClobberProcPtr doClobber)
  2020. {
  2021. WHHandle    wh;
  2022. short    propType;
  2023.  
  2024.     if (!SkelWindow (dlog, nil, nil, nil, nil, doClose, doClobber, nil, true))
  2025.         return (false);
  2026.  
  2027.     /*
  2028.      * Determine dialog property type to add to window and add the property,
  2029.      * unless it already has one.  (The property might already exist if
  2030.      * the handler for an existing window is being replaced.)
  2031.      */
  2032.     propType = (SkelIsMMDlog (dlog) ? skelWPropMovableModal : skelWPropModeless);
  2033.     if (SkelGetWindProp (dlog, propType) == (SkelWindPropHandle) nil
  2034.         && !SkelAddWindProp (dlog, propType, (long) 0))
  2035.     {
  2036.         SkelRmveDlog (dlog);
  2037.         return (false);
  2038.     }
  2039.  
  2040.     wh = GetWHandler (dlog);
  2041.     (**wh).whSelect = doSelect;
  2042.     (**wh).whFilter = doFilter;
  2043.     return (true);
  2044. }
  2045.  
  2046.  
  2047. /*
  2048.  * Remove a dialog and its handler
  2049.  */
  2050.  
  2051. pascal void
  2052. SkelRmveDlog (DialogPtr dlog)
  2053. {
  2054.     SkelRmveWind (dlog);
  2055. }
  2056.  
  2057.  
  2058. /*
  2059.  * Override the default sizing limits for a window, or, if w
  2060.  * is nil, reset the default limits used by SkelWindow.
  2061.  */
  2062.  
  2063. pascal void
  2064. SkelSetGrowBounds (WindowPtr w, short hLo, short vLo, short hHi, short vHi)
  2065. {
  2066. WHHandle    wh;
  2067. Rect        r;
  2068.  
  2069.     if (w == (WindowPtr) nil)
  2070.         SetRect (&growRect, hLo, vLo, hHi, vHi);
  2071.     else if ((wh = GetWHandler (w)) != (WHHandle) nil)
  2072.     {
  2073.         SetRect (&r, hLo, vLo, hHi, vHi);
  2074.         (**wh).whGrow = r;
  2075.     }
  2076. }
  2077.  
  2078.  
  2079. pascal void 
  2080. SkelSetZoom (WindowPtr w, SkelWindZoomProcPtr pZoom)
  2081. {
  2082. WHHandle    h;
  2083.  
  2084.     if (w == (WindowPtr) nil)
  2085.         zoomProc = pZoom;
  2086.     else if ((h = GetWHandler (w)) != (WHHandle) nil)
  2087.         (**h).whZoom = pZoom;
  2088. }
  2089.  
  2090.  
  2091. /*
  2092.  * Return zoom proc associated with window, nil if there isn't one.
  2093.  * Return default zoom proc if window is nil.
  2094.  */
  2095.  
  2096. pascal SkelWindZoomProcPtr
  2097. SkelGetZoom (WindowPtr w)
  2098. {
  2099. WHHandle    h;
  2100.  
  2101.     if (w == (WindowPtr) nil)
  2102.         return (zoomProc);
  2103.     if ((h = GetWHandler (w)) != (WHHandle) nil)
  2104.         return ((**h).whZoom);
  2105.     return ((SkelWindZoomProcPtr) nil);
  2106. }
  2107.  
  2108.  
  2109. pascal Boolean
  2110. SkelWindowRegistered (WindowPtr w)
  2111. {
  2112.     return ((Boolean) (GetWHandler (w) != (WHHandle) nil));
  2113. }
  2114.  
  2115.  
  2116. /*
  2117.  * Routines to determine whether a given window is a dialog, or a movable
  2118.  * modal dialog.  Safe to pass nil.
  2119.  */
  2120.  
  2121. pascal Boolean
  2122. SkelIsDlog (WindowPtr w)
  2123. {
  2124.     return (w != (WindowPtr) nil && ((WindowPeek)w)->windowKind == dialogKind);
  2125. }
  2126.  
  2127.  
  2128. pascal Boolean
  2129. SkelIsMMDlog (WindowPtr w)
  2130. {
  2131.     return (SkelIsDlog (w) && hasGetWVariant && GetWVariant (w) == movableDBoxProc);
  2132. }
  2133.  
  2134.  
  2135. /* ------------------------ */
  2136. /* Handler finders/removers */
  2137. /* ------------------------ */
  2138.  
  2139. /*
  2140.  * Get handler associated with a window.
  2141.  *
  2142.  * Return nil if window doesn't belong to any known handler.
  2143.  *
  2144.  * This routine is absolutely fundamental to TransSkel.
  2145.  */
  2146.  
  2147.  
  2148. static WHHandle
  2149. GetWHandler (WindowPtr w)
  2150. {
  2151. WHHandle    h;
  2152.  
  2153.     if (w == (WindowPtr) nil)
  2154.         return ((WHHandle) nil);
  2155.  
  2156.     if (w == oldWindow) 
  2157.         return (oldWHandler);        /* return handler of cached window */
  2158.  
  2159.     for (h = whList; h != (WHHandle) nil; h = (**h).whNext)
  2160.     {
  2161.         if ((**h).whWind == w)
  2162.         {
  2163.             oldWindow = w;    /* set cached window and handler */
  2164.             oldWHandler = h;
  2165.             return (h);
  2166.         }
  2167.     }
  2168.     return ((WHHandle) nil);
  2169. }
  2170.  
  2171.  
  2172. /*
  2173.  * Detach a handler from the handler list and destroy it.
  2174.  *
  2175.  * Clear window cache variable, just in case it points to the window
  2176.  * whose hander is being destroyed (and thus has become invalid).
  2177.  */
  2178.  
  2179. static void
  2180. DetachWHandler (WHHandle wh)
  2181. {
  2182. WHHandle    h, h2;
  2183.  
  2184.     if (whList != (WHHandle) nil)        /* if list empty, ignore */
  2185.     {
  2186.         if (whList == wh)                /* is it the first element? */
  2187.         {
  2188.             h2 = whList;
  2189.             whList = (**whList).whNext;
  2190.         }
  2191.         else for (h = whList; h != (WHHandle) nil; h = h2)
  2192.         {
  2193.             h2 = (**h).whNext;
  2194.             if (h2 == (WHHandle) nil)
  2195.                 return;                    /* handler not in list! (huh?) */
  2196.             if (h2 == wh)                /* found it */
  2197.             {
  2198.                 (**h).whNext = (**h2).whNext;
  2199.                 break;
  2200.             }
  2201.         }
  2202.         DisposeHandle ((Handle) h2);        /* get rid of handler record */
  2203.     }
  2204.  
  2205.     oldWindow = (WindowPtr) nil;    /* clear window cache variables */
  2206.     oldWHandler = (WHHandle) nil;
  2207. }
  2208.  
  2209.  
  2210. /* ------------------------------------------------------- */
  2211. /* Menu handler installation/removal/modification routines */
  2212. /* ------------------------------------------------------- */
  2213.  
  2214.  
  2215. /*
  2216.  * Install handler for a menu.  Remove any previous handler for it.
  2217.  * Pass the following parameters:
  2218.  *
  2219.  * theMenu
  2220.  *        Handle to the menu to be handled.  Must be created by host.
  2221.  * doSelect
  2222.  *        Proc that handles selection of items from menu.  If this is
  2223.  *         nil, the menu is installed, but nothing happens when items
  2224.  *         are selected from it.
  2225.  * doClobber
  2226.  *        Proc for disposal of handler's data structures.  Usually
  2227.  *         nil for menus that remain in menu bar until program
  2228.  *         termination.
  2229.  * subMenu
  2230.  *        True if the menu is a submenu (not installed in menu bar).
  2231.  * drawBar
  2232.  *        True if menu bar is to be drawn after menu is installed.
  2233.  *         (Ignored if the menu is a submenu.)
  2234.  *
  2235.  * Return true if successful, false if no handler could be allocated.
  2236.  */
  2237.  
  2238. pascal Boolean
  2239. SkelMenu (MenuHandle m,
  2240.             SkelMenuSelectProcPtr doSelect,
  2241.             SkelMenuClobberProcPtr doClobber,
  2242.             Boolean subMenu,
  2243.             Boolean drawBar)
  2244. {
  2245. MHHandle    mh;
  2246. Boolean        oldFlag;
  2247.  
  2248.     oldFlag = mhClobOnRmve;        /* remove any previous handler for */
  2249.     mhClobOnRmve = false;        /* menu, without redrawing menu bar */
  2250.     SkelRmveMenu (m);
  2251.     mhClobOnRmve = oldFlag;
  2252.  
  2253.     if ((mh = New (MHandler)) != (MHHandle) nil)
  2254.     {
  2255.         (**mh).mhNext = mhList;
  2256.         mhList = mh;
  2257.         (**mh).mhID = (**m).menuID;    /* get menu id number */
  2258.         (**mh).mhSelect = doSelect;            /* install selection handler */
  2259.         (**mh).mhClobber = doClobber;        /* install disposal handler */
  2260.         (**mh).mhSubMenu = subMenu;            /* set submenu flag */
  2261.         /* install menu in menu bar if not a submenu */
  2262.         InsertMenu (m, subMenu ? -1 : 0);
  2263.     }
  2264.     if (drawBar && !subMenu)
  2265.         DrawMenuBar ();
  2266.     return ((Boolean) (mh != (MHHandle) nil));
  2267. }
  2268.  
  2269.  
  2270. /*
  2271.  * Remove a menu handler.  This calls the handler's menu disposal
  2272.  * routine and then takes the handler out of the handler list and
  2273.  * disposes of it.  The menu bar is redrawn if the menu was not a
  2274.  * submenu and the global redraw flag hasn't been cleared.
  2275.  *
  2276.  * The menu MUST be deleted from the menu bar before calling the
  2277.  * clobber proc.  Otherwise the menu bar will end up filled with
  2278.  * garbage if the menu was allocated with NewMenu (IM I-352).
  2279.  */
  2280.  
  2281. pascal void
  2282. SkelRmveMenu (MenuHandle m)
  2283. {
  2284. short    mID;
  2285. MHHandle    h, h2;
  2286. SkelMenuClobberProcPtr    p;
  2287.  
  2288.     mID = (**m).menuID;
  2289.     if (mhList != (MHHandle) nil)            /* if list empty, ignore */
  2290.     {
  2291.         if ((**mhList).mhID == mID)    /* is it the first element? */
  2292.         {
  2293.             h2 = mhList;
  2294.             mhList = (**mhList).mhNext;
  2295.         }
  2296.         else
  2297.         {
  2298.             for (h = mhList; h != (MHHandle) nil; h = h2)
  2299.             {
  2300.                 h2 = (**h).mhNext;
  2301.                 if (h2 == (MHHandle) nil)
  2302.                     return;                        /* menu not in list! */
  2303.                 if ((**h2).mhID == mID)            /* found it */
  2304.                 {
  2305.                     (**h).mhNext = (**h2).mhNext;
  2306.                     break;
  2307.                 }
  2308.             }
  2309.         }
  2310.         DeleteMenu (mID);
  2311.         if (mhDrawBarOnRmve && !(**h2).mhSubMenu)
  2312.             DrawMenuBar ();
  2313.         if (mhClobOnRmve
  2314.             && (p = (**h2).mhClobber) != (SkelMenuClobberProcPtr) nil)
  2315.                 (*p) (m);                    /* call disposal routine */
  2316.         DisposeHandle ((Handle) h2);        /* get rid of handler record */
  2317.     }
  2318. }
  2319.  
  2320.  
  2321. /*
  2322.  * General menu-selection handler.  Just passes selection to the handler's
  2323.  * select routine.  If the select routine is nil, selecting items from
  2324.  * the menu is a nop.
  2325.  */
  2326.  
  2327. static void
  2328. DoMenuCommand (long command)
  2329. {
  2330. short        menu;
  2331. short        item;
  2332. MHHandle    mh;
  2333.  
  2334.     menu = HiWord (command);
  2335.     item = LoWord (command);
  2336.     for (mh = mhList; mh != (MHHandle) nil; mh = (**mh).mhNext)
  2337.     {
  2338.         if (menu == (**mh).mhID && (**mh).mhSelect != (SkelMenuSelectProcPtr) nil)
  2339.         {
  2340.             (*(**mh).mhSelect) (item);
  2341.             break;
  2342.         }
  2343.     }
  2344.     HiliteMenu (0);        /* command done, turn off menu hiliting */
  2345. }
  2346.  
  2347.  
  2348. /*
  2349.  * Menu is about to be pulled down or command-key executed.  Call menu
  2350.  * hook if there is one so application can set menus/items appropriately.
  2351.  */
  2352.  
  2353. static void
  2354. DoMenuHook (void)
  2355. {
  2356.     if (pMenuHook != (SkelMenuHookProcPtr) nil)
  2357.         (*pMenuHook) ();
  2358. }
  2359.  
  2360.  
  2361. pascal void
  2362. SkelSetMenuHook (SkelMenuHookProcPtr p)
  2363. {
  2364.     pMenuHook = p;
  2365. }
  2366.  
  2367.  
  2368. pascal SkelMenuHookProcPtr
  2369. SkelGetMenuHook (void)
  2370. {
  2371.     return (pMenuHook);
  2372. }
  2373.  
  2374.  
  2375. /* ------------------------ */
  2376. /* Window property routines */
  2377. /* ------------------------ */
  2378.  
  2379.  
  2380. /*
  2381.  * Add a property to a window.  Fail if the window is unregistered
  2382.  * or can't allocate memory for a new property structure.  If the
  2383.  * window already has such a property, fail.
  2384.  *
  2385.  * Returns a handle to the new property for success, nil for failure.
  2386.  */
  2387.  
  2388. pascal Boolean
  2389. SkelAddWindProp (WindowPtr w, short propType, long propData)
  2390. {
  2391. WHHandle        wh;
  2392. SkelWindPropHandle    ph;
  2393.  
  2394.     if (propType == skelWPropAll)
  2395.         return (false);
  2396.     if ((ph = SkelGetWindProp (w, propType)) != (SkelWindPropHandle) nil)
  2397.         return (false);
  2398.     /* if window is unregistered, or can't allocate structure, fail */
  2399.     if ((wh = GetWHandler (w)) == (WHHandle) nil
  2400.         || (ph = New (SkelWindProperty)) == (SkelWindPropHandle) nil)
  2401.         return (false);
  2402.     (**ph).skelWPropType = propType;
  2403.     (**ph).skelWPropData = propData;
  2404.     (**ph).skelWPropNext = (**wh).whProperties;
  2405.     (**wh).whProperties = ph;
  2406.     return (true);
  2407. }
  2408.  
  2409.  
  2410. /*
  2411.  * Remove a window property.  Does nothing if the window isn't
  2412.  * registered or if the window doesn't have the given property.
  2413.  *
  2414.  * If propType is skelWPropAll, SkelRmveWindProp() calls itself
  2415.  * recursively to remove all the properties on a window.  This
  2416.  * means that if you put skelWPropAll into the skelWPropType field
  2417.  * of a property, you'll get an infinite loop here.
  2418.  */
  2419.  
  2420. pascal void
  2421. SkelRmveWindProp (WindowPtr w, short propType)
  2422. {
  2423. WHHandle        wh;
  2424. SkelWindPropHandle    ph, ph2, pNext;
  2425.  
  2426.     if ((wh = GetWHandler (w)) == (WHHandle) nil
  2427.         || (ph = SkelGetWindProp (w, propType)) == (SkelWindPropHandle) nil)
  2428.         return;
  2429.  
  2430.     if (propType == skelWPropAll)    /* remove all properties */
  2431.     {
  2432.         while ((ph = (**wh).whProperties) != (SkelWindPropHandle) nil)
  2433.             SkelRmveWindProp (w, (**ph).skelWPropType);
  2434.         return;
  2435.     }
  2436.  
  2437.     /* remove particular property */
  2438.     if ((ph2 = (**wh).whProperties) == ph)    /* remove first in list */
  2439.         (**wh).whProperties = (**ph).skelWPropNext;
  2440.     else
  2441.     {
  2442.         while ((pNext = (**ph2).skelWPropNext) != (SkelWindPropHandle) nil)
  2443.         {
  2444.             if (pNext == ph)
  2445.             {
  2446.                 (**ph2).skelWPropNext = (**ph).skelWPropNext;
  2447.                 break;
  2448.             }
  2449.             ph2 = pNext;
  2450.         }
  2451.     }
  2452.     DisposeHandle ((Handle) ph);
  2453. }
  2454.  
  2455.  
  2456. /*
  2457.  * Find the given property for the window.  Fail if window is
  2458.  * unregistered or has no such property.
  2459.  */
  2460.  
  2461. pascal SkelWindPropHandle
  2462. SkelGetWindProp (WindowPtr w, short propType)
  2463. {
  2464. WHHandle            wh;
  2465. SkelWindPropHandle    ph = (SkelWindPropHandle) nil;
  2466.  
  2467.     if ((wh = GetWHandler (w)) != (WHHandle) nil)
  2468.     {
  2469.         if (propType == skelWPropAll)    /* return head of list */
  2470.             ph = (**wh).whProperties;
  2471.         else for (ph = (**wh).whProperties; ph != (SkelWindPropHandle) nil; ph = (**ph).skelWPropNext)
  2472.         {
  2473.             if ((**ph).skelWPropType == propType)
  2474.                 break;
  2475.         }
  2476.     }
  2477.     return (ph);
  2478. }
  2479.  
  2480.  
  2481. /*
  2482.  * Find the data value for a given property for the window.  Return 0 if window
  2483.  * is unregistered or has no such property.
  2484.  *
  2485.  * If you need to be able to distinquish an error return from a valid zero-value
  2486.  * data value, you should call SkelGetWindProp() instead, check for an error,
  2487.  * and extract the data value if there was no error.
  2488.  *
  2489.  * skelWPropAll is not a valid properly type for this call.
  2490.  */
  2491.  
  2492. pascal long
  2493. SkelGetWindPropData (WindowPtr w, short propType)
  2494. {
  2495. SkelWindPropHandle    ph;
  2496.  
  2497.     ph = SkelGetWindProp (w, propType);
  2498.     if (ph != (SkelWindPropHandle) nil)
  2499.         return ((**ph).skelWPropData);
  2500.     return (0);
  2501. }
  2502.